﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Linq.Expressions;
using System.Dynamic;
using System.Runtime.CompilerServices;

using Alicanto.Compilation;

namespace Alicanto.Execution
{
    internal class ExecutionContextMember
    {
        public object Member { get; set; }
        public Scope Scope { get; private set; }

        internal ExecutionContextMember(object member)
        {
            this.Member = member;
        }

        internal ExecutionContextMember(object member, Scope scope)
        {
            this.Member = member;
            this.Scope = scope;
        }

        internal ExecutionContextMember Compile(ExecutionContextInstance instance)
        {
            object member = this.Member;
            if (this.Member is Expression)
            {
                ExpressionBinder binder = new ExpressionBinder(instance);
                member = binder.Visit(this.Member as Expression);
            }

            return new ExecutionContextMember(member);
        }

        class ExpressionBinder : ExpressionVisitor
        {
            private Dictionary<ParameterExpression, ParameterExpression> _parameters;
            private ExecutionContextInstance _instance;

            internal ExpressionBinder(ExecutionContextInstance instance)
            {
                _parameters = new Dictionary<ParameterExpression, ParameterExpression>();
                _instance = instance;
            }

            public override Expression Visit(Expression node)
            {
                if (node.NodeType == ExpressionType.Lambda)
                {
                    return VisitLambda(node as LambdaExpression);
                }

                return base.Visit(node);
            }

            protected Expression VisitLambda(LambdaExpression node)
            {
                IList<ParameterExpression> parameters = new List<ParameterExpression>();
                foreach (ParameterExpression parameter in node.Parameters)
                    parameters.Add((ParameterExpression)this.Visit(parameter));

                return Expression.Lambda(
                    this.Visit(node.Body),
                    parameters
                );
            }

            protected override Expression VisitBinary(BinaryExpression node)
            {
                return Expression.MakeBinary(node.NodeType, this.Visit(node.Left), this.Visit(node.Right));
            }

            protected override Expression VisitBlock(BlockExpression node)
            {
                IList<ParameterExpression> variables = new List<ParameterExpression>();
                foreach (ParameterExpression variable in node.Variables)
                    variables.Add((ParameterExpression)this.Visit(variable));

                IList<Expression> expressions = new List<Expression>();
                foreach (Expression expression in node.Expressions)
                    expressions.Add(this.Visit(expression));

                return Expression.Block(
                    variables,
                    expressions
                );
            }

            protected override CatchBlock VisitCatchBlock(CatchBlock node)
            {
                return base.VisitCatchBlock(node);
            }

            protected override Expression VisitConditional(ConditionalExpression node)
            {
                if (node.IfFalse != null)
                    return Expression.IfThenElse(
                        this.Visit(node.Test),
                        this.Visit(node.IfTrue),
                        this.Visit(node.IfFalse)
                    );
                else
                    return Expression.IfThen(
                        this.Visit(node.Test),
                        this.Visit(node.IfTrue)
                    );
            }

            protected override Expression VisitConstant(ConstantExpression node)
            {
                if (node.Value == _instance.Clazz)
                {
                    return Expression.Constant(_instance);
                }
                else
                    return node;
            }

            protected override Expression VisitDebugInfo(DebugInfoExpression node)
            {
                return base.VisitDebugInfo(node);
            }

            protected override Expression VisitDefault(DefaultExpression node)
            {
                return base.VisitDefault(node);
            }

            protected override Expression VisitDynamic(DynamicExpression node)
            {
                IList<Expression> arguments = new List<Expression>();
                foreach(Expression argument in node.Arguments)
                    arguments.Add(this.Visit(argument));

                return Expression.Dynamic(
                    node.Binder,
                    node.Type,
                    arguments
                );
            }

            protected override ElementInit VisitElementInit(ElementInit node)
            {
                throw new NotImplementedException();
            }

            protected override Expression VisitExtension(Expression node)
            {
                throw new NotImplementedException();
            }

            protected override Expression VisitGoto(GotoExpression node)
            {
                throw new NotImplementedException();
            }

            protected override Expression VisitIndex(IndexExpression node)
            {
                throw new NotImplementedException();
            }

            protected override Expression VisitInvocation(InvocationExpression node)
            {
                throw new NotImplementedException();
            }

            protected override Expression VisitLabel(LabelExpression node)
            {
                return Expression.Label(
                    node.Target,
                    node.DefaultValue
                );
            }

            protected override LabelTarget VisitLabelTarget(LabelTarget node)
            {
                throw new NotImplementedException();
            }

            protected override Expression VisitListInit(ListInitExpression node)
            {
                throw new NotImplementedException();
            }

            protected override Expression VisitLoop(LoopExpression node)
            {
                throw new NotImplementedException();
            }

            protected override Expression VisitMember(MemberExpression node)
            {
                throw new NotImplementedException();
            }

            protected override MemberAssignment VisitMemberAssignment(MemberAssignment node)
            {
                throw new NotImplementedException();
            }

            protected override MemberBinding VisitMemberBinding(MemberBinding node)
            {
                throw new NotImplementedException();
            }

            protected override Expression VisitMemberInit(MemberInitExpression node)
            {
                throw new NotImplementedException();
            }

            protected override MemberListBinding VisitMemberListBinding(MemberListBinding node)
            {
                throw new NotImplementedException();
            }

            protected override MemberMemberBinding VisitMemberMemberBinding(MemberMemberBinding node)
            {
                throw new NotImplementedException();
            }

            protected override Expression VisitMethodCall(MethodCallExpression node)
            {
                IList<Expression> arguments = new List<Expression>();
                foreach (Expression argument in node.Arguments)
                    arguments.Add(this.Visit(argument));

                if (node.Object != null)
                    return Expression.Call(
                        this.Visit(node.Object),
                        node.Method,
                        arguments
                    );
                else
                    return Expression.Call(
                        null,
                        node.Method,
                        arguments
                    );
            }

            protected override Expression VisitNew(NewExpression node)
            {
                throw new NotImplementedException();
            }

            protected override Expression VisitNewArray(NewArrayExpression node)
            {
                throw new NotImplementedException();
            }

            protected override Expression VisitParameter(ParameterExpression node)
            {
                if (!_parameters.ContainsKey(node))
                {
                    _parameters[node] = Expression.Parameter(
                        node.Type,
                        node.Name
                    );
                }

                return _parameters[node];
            }

            protected override Expression VisitRuntimeVariables(RuntimeVariablesExpression node)
            {
                IList<ParameterExpression> variables = new List<ParameterExpression>();
                foreach (ParameterExpression variable in node.Variables)
                    variables.Add((ParameterExpression)this.Visit(variable));

                return Expression.RuntimeVariables(
                    variables
                );
            }

            protected override Expression VisitSwitch(SwitchExpression node)
            {
                throw new NotImplementedException();
            }

            protected override SwitchCase VisitSwitchCase(SwitchCase node)
            {
                throw new NotImplementedException();
            }

            protected override Expression VisitTry(TryExpression node)
            {
                throw new NotImplementedException();
            }

            protected override Expression VisitTypeBinary(TypeBinaryExpression node)
            {
                throw new NotImplementedException();
            }

            protected override Expression VisitUnary(UnaryExpression node)
            {
                return Expression.MakeUnary(
                    node.NodeType,
                    node.Operand,
                    node.Type
                );
            }
        }
    }
}
